寫到資料庫這部分,就讓我想起一個小故事...
某天客戶一把鼻涕一把眼淚地打電話跟我說
客戶: 我們要結帳了,可是有些單子裡面的項目有少怎麼辦?
這不查不打緊,一查才發現,客戶公司有一陣子網路不穩定,
轉資料的時候,常常轉到一半網路掛掉,導致某些時段的單子的資料只轉了一半。
看完資料後我也只想說.....涼拌炒雞蛋,愈炒愈完蛋。
很多剛開始做開發的新人常常會將SQL語法拆開來,很單純的一個命令一個命令送,
導致我們很難去判別,每一筆(交易)資料是否完整。
若想解決這個問題,我們可以透過資料庫ACID特性中的原子性(Atomicity),
來達到確保每筆資料的完整。
建立需要與資料庫的Table相對應的Class
Menulistb.cs
public class Menulistb
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public long Uid { get; set; }
public long H_uid { get; set; }
public string Item { get; set; }
public int Price { get; set; }
public int Count { get; set; }
}
Menulisth.cs
public class Menulisth
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public long Uid { get; set; }
public string Formnum { get; set; }
[SugarColumn(IsOnlyIgnoreInsert = true)]
public int Createtime { get; set; }
}
這邊要注意Createtime
上給的屬性**[SugarColumn(IsOnlyIgnoreInsert = true)]**
因為我們之後會直接從資料庫給這個欄位default值,所以需要設立這個屬性。
ALTER TABLE [dbo].[menulisth]
ADD CONSTRAINT DF_Menulisth_Createtime
DEFAULT CURRENT_TIMESTAMP FOR [createtime]
這樣每當我們新增一筆資料的時候,就會自動把當下時間帶入到createtime
裡面。
上一篇中已經把資料從前端送到後端來了,
我們接著把傳過來的資料處理一下轉成對應的object。
1.將 傳過來的data 轉成 object
Dictionary<string, Menulistb> map = JsonConvert.DeserializeObject<Dictionary<string, Menulistb>>(data);
從Json轉成Object 無非就四樣:
稍微注意一下資料的結構就會轉了。
2. 把剛剛上面那段加入我們後台處理問題的function中 CreateOrder()
public ActionResult CreateOrder(string data,string phone)
{
// 表頭部分資料
var menulisth = new Menulisth()
{
Formnum = phone
};
Dictionary<string, Menulistb> map = JsonConvert.DeserializeObject<Dictionary<string, Menulistb>>(data);
return Ok();
}
//連線設定
SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
{
//連線字串
ConnectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WebMenu;user id=webmenu;password=xxxxxxxx;Integrated Security=False",
DbType = DbType.SqlServer,//連線類型
IsAutoCloseConnection = true //自動關閉連線
});
try
{
//當執行時,觸發事件
db.Aop.OnLogExecuting = (sql, pars) =>
{
Console.WriteLine(sql);//查看SQL語法
};
db.BeginTran();
// dosomething
db.CommitTran();
}
catch
{
db.RollbackTran();//rollback
throw;
}
當我們資料出問題的時候會退回到原本的狀態,並且打印我們SQL語法的資訊
3. 在 dosomething 的地方寫我們要寫入資料庫的語法
db.BeginTran();
//寫入表頭 並 回傳表頭資料
menulisth = db.Insertable(menulisth).ExecuteReturnEntity();
//逐筆將表身資料寫入
foreach (var keyvalue in map)
{
var item = keyvalue.Value;
Console.WriteLine(item.Uid + " : " + item.Item + " : " + item.Price + " : " + item.Count);
item.H_uid = menulisth.Uid;
db.Insertable(item).ExecuteCommand();
}
db.CommitTran();
這樣就成功將資料寫入我們資料庫了。
CreateOrder()中完整程式碼:
public ActionResult CreateOrder(string data,string phone)
{
var menulisth = new Menulisth()
{
Formnum = phone
};
Dictionary<string, Menulistb> map = JsonConvert.DeserializeObject<Dictionary<string, Menulistb>>(data);
//連線設定
SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
{
//連線字串
ConnectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WebMenu;user id=webmenu;password=xxxxxxxx;Integrated Security=False",
DbType = DbType.SqlServer,//連線類型
IsAutoCloseConnection = true //自動關閉連線
});
try
{
//當執行時,觸發事件
db.Aop.OnLogExecuting = (sql, pars) =>
{
Console.WriteLine(sql);//查看SQL語法
};
db.BeginTran();
//寫入表頭 並 回傳表頭資料
menulisth = db.Insertable(menulisth).ExecuteReturnEntity();
//逐筆將表身資料寫入
foreach (var keyvalue in map)
{
var item = keyvalue.Value;
Console.WriteLine(item.Uid + " : " + item.Item + " : " + item.Price + " : " + item.Count);
item.H_uid = menulisth.Uid;
db.Insertable(item).ExecuteCommand();
}
db.CommitTran();
}
catch
{
db.RollbackTran();//rollback
throw;
}
return Ok();
}
我們可以透過 begin tran
... commit tran
將所有要完成的指令一次做完,
這樣可以保證我們資料每次進入資料庫的時候都是完整的。
另外可能有些人會對時間資訊應該要由資料庫直接產生好,還是從程式碼中產生好的疑問。
這邊給一個思考的方向,當程式碼成長到上萬行,表單變成上千個的時候,
放在程式碼中,還是資料庫裡哪個比較好維護?